package http_proxy_middleware

import (
	"fmt"
	"runtime/debug"

	"github.com/afex/hystrix-go/hystrix"
	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
	"github.com/youkelike/gateway/core/middleware"
	"github.com/youkelike/gateway/dao"
	"github.com/youkelike/gateway/public"
)

func HTTPCircuitMiddleware() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		serviceInterface, ok := ctx.Get("service")
		if !ok {
			middleware.ResponseError(ctx, 2001, errors.New("service not found"))
			ctx.Abort()
			return
		}
		serviceDetail := serviceInterface.(*dao.ServiceDetail)
		// fmt.Println(public.Obj2Json(serviceDetail.CircuitConfig))
		if serviceDetail.CircuitConfig.NeedCircuit == 1 {
			// 在第一个 callback 执行业务，返回的错误会传给第二个 callback，
			// 在第二个 callback 执行降级逻辑，这里返回的错误会作为 Do 方法的返回值，表示降级失败，
			// 当前业务不会有降级失败情况，
			hystrix.Do(serviceDetail.Info.ServiceName, func() (err error) {
				// 必须在这里处理后续 Next 中的 panic
				defer func() {
					if e := recover(); e != nil {
						public.ComLogWarning(ctx, "_com_panic", map[string]interface{}{
							"uri":    ctx.Request.RequestURI,
							"method": ctx.Request.Method,
							"args":   ctx.Request.PostForm,
							"from":   ctx.ClientIP(),
							"stack":  string(debug.Stack()),
						})

						fmt.Println("hystrix panic<<<<<", e)
						err = fmt.Errorf("%v", e)
					}
				}()
				ctx.Next()

				// 这里一定要注意处理 key 不存在的情况，否则会出现空指针异常
				response, ok := ctx.Get("response")
				if !ok {
					// 代理服务正常，会走到这里
					// fmt.Println("hystrix runFunc success")
					return
				}

				resp, _ := response.(*middleware.Response)
				if resp.ErrorCode != middleware.SuccessCode {
					// 代理某个流程出错，会走到这里
					fmt.Println("hystrix runFunc err<<<<<", resp.ErrorMsg)
					return errors.New(resp.ErrorMsg)
				}

				return nil
			}, func(err error) error {
				// 熔断开启、超过限流、业务代码执行超时、第一个 callback 返回 error 都会执行到这里
				middleware.ResponseError(ctx, 50000, errors.New(serviceDetail.CircuitConfig.FallBackMsg))
				fmt.Println("hystrix fallbackFunc error====", err.Error())
				ctx.Abort()
				return nil
			})

			return
		}
		ctx.Next()
	}
}
